package com.bestcem.xm.common.core.sql;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bestcem.xm.common.core.constant.ErrorCodes;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.exception.BaseRuntimeException;
import com.bestcem.xm.common.core.uitls.DecoderUtil;
import com.bestcem.xm.common.core.uitls.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.BiConsumer;

@Slf4j
public class SQLConditionCompiler {
    private static final Map<String, Map<String, BiConsumer<ConditionProxy, QueryWrapper<Object>>>> supportedTypes = new HashMap<>();

    // 初始化支持的数据类型
    static {
        // 字符串类型
        Map<String, BiConsumer<ConditionProxy, QueryWrapper<Object>>> supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> wrapper.eq(proxy.getName(),
                proxy.getStringValue()));
        supportedOperators.put(ConditionConstant.Operators.NE, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().ne(proxy.getName(), proxy.getStringValue()));
        supportedOperators.put(ConditionConstant.Operators.REGEX, (proxy, wrapper) -> {
            String template = String.format("%s regexp {0}", proxy.getName());
            wrapper.apply(template, proxy.getStringValue());
        });
        supportedOperators.put(ConditionConstant.Operators.NREGEX, (proxy, wrapper) -> {
            String template = String.format("%s not regexp {0}", proxy.getName());
            wrapper.isNull(proxy.getName()).or().apply(template, proxy.getStringValue());
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().eq(proxy.getName(), StringUtils.EMPTY));
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> wrapper.isNotNull(proxy.getName())
                .ne(proxy.getName(), StringUtils.EMPTY));
        supportedTypes.put(ConditionConstant.FieldTypes.STRING, supportedOperators);

        // 数字类型
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.GT, (proxy, wrapper) -> wrapper.gt(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.GTE, (proxy, wrapper) -> wrapper.ge(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.LT, (proxy, wrapper) -> wrapper.lt(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.LTE, (proxy, wrapper) -> wrapper.le(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> wrapper.eq(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.NE, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().ne(proxy.getName(), proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.RANGE, (proxy, wrapper) -> {
            List<Object> values = proxy.listValues();
            if (CollectionUtils.isEmpty(values) || values.size() != 2) {
                throw new ConditionException(String.format("condition.value %s illegal", values));
            }

            Object leftValue = values.get(0);
            Object rightValue = values.get(1);
            if (leftValue == null || rightValue == null) {
                String msg = String.format("condition.value missing for $range, left: %s, right: %s",
                        leftValue, rightValue);
                throw new ConditionException(msg);
            }

            String leftOperator = proxy.isLeftOpen() ? ">" : ">=";
            String rightOperator = proxy.isRightOpen() ? "<" : "<=";
            StringBuilder buf = new StringBuilder();
            buf.append(proxy.getName()).append(StringUtils.SPACE).append(leftOperator).append(StringUtils.SPACE)
                    .append("{0}");
            buf.append(StringUtils.SPACE).append("and").append(StringUtils.SPACE);
            buf.append(proxy.getName()).append(StringUtils.SPACE).append(rightOperator).append(StringUtils.SPACE)
                    .append("{1}");
            wrapper.apply(buf.toString(), leftValue, rightValue);
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().eq(proxy.getName(), 0));
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> wrapper
                .isNotNull(proxy.getName()).ne(proxy.getName(), 0));
        supportedTypes.put(ConditionConstant.FieldTypes.NUMBER, supportedOperators);

        // 日期
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.RANGE, (proxy, wrapper) -> {
            List<Object> values = proxy.listValues();
            if (CollectionUtils.isEmpty(values) || values.size() != 2) {
                throw new ConditionException(String.format("condition.value %s illegal", values));
            }

            Object leftValue = values.get(0);
            Object rightValue = values.get(1);
            if (leftValue == null || rightValue == null) {
                String msg = String.format("condition.value missing for $range, left: %s, right: %s",
                        leftValue, rightValue);
                throw new ConditionException(msg);
            }

            String leftOperator = proxy.isLeftOpen() ? ">" : ">=";
            String rightOperator = proxy.isRightOpen() ? "<" : "<=";
            StringBuilder buf = new StringBuilder();
            buf.append(proxy.getName()).append(StringUtils.SPACE).append(leftOperator).append(StringUtils.SPACE)
                    .append("{0}");
            buf.append(StringUtils.SPACE).append("and").append(StringUtils.SPACE);
            buf.append(proxy.getName()).append(StringUtils.SPACE).append(rightOperator).append(StringUtils.SPACE)
                    .append("{1}");
            wrapper.apply(buf.toString(), leftValue, rightValue);
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> wrapper
                .isNull(proxy.getName()));
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> wrapper
                .isNotNull(proxy.getName()));
        supportedTypes.put(ConditionConstant.FieldTypes.DATETIME, supportedOperators);

        // 选择
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> wrapper.eq(proxy.getName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.NE, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().ne(proxy.getName(), proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.IN, (proxy, wrapper) -> wrapper.in(proxy.getName(),
                proxy.listValues()));
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> wrapper.isNull(proxy.getName())
                .or().eq(proxy.getName(), StringUtils.EMPTY));
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> wrapper.isNotNull(proxy.getName())
                .ne(proxy.getName(), StringUtils.EMPTY));
        supportedTypes.put(ConditionConstant.FieldTypes.SELECT, supportedOperators);

        // 层级
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> {
            List<Object> values = proxy.listValues();
            if (CollectionUtils.isEmpty(values) || values.size() < 1) {
                throw new ConditionException(String.format("condition.value %s illegal", values));
            }

            Object endValue = values.get(values.size() - 1);
            if (endValue == null) {
                String msg = String.format("condition.value missing for level, endValue: %s", endValue);
                throw new ConditionException(msg);
            }

            wrapper.eq(proxy.getName(), endValue);
        });
        supportedOperators.put(ConditionConstant.Operators.INCLUDE, (proxy, wrapper) -> {
            List<Object> values = proxy.listValues();
            if (CollectionUtils.isEmpty(values) || values.size() < 1) {
                throw new ConditionException(String.format("condition.value %s illegal", values));
            }

            Object endValue = values.get(values.size() - 1);
            if (endValue == null) {
                String msg = String.format("condition.value missing for level, endValue: %s", endValue);
                throw new ConditionException(msg);
            }
            String sql = "%s in (select group_id from " +
                    "ds_member_user_group where group_id = ds_member_member.store_id " +
                    "and status = 0 and json_contains(parent_ids, concat('\\\"',{0},'\\\"'), '$'))";
            String template = String.format(sql, proxy.getName());
            wrapper.apply(template, StringUtil.toHexString((byte[]) endValue));
        });
        supportedTypes.put(ConditionConstant.FieldTypes.LEVEL, supportedOperators);

        // label
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> {
            String template = String.format("JSON_CONTAINS(%s, JSON_OBJECT('id', {0}, 'label_id', {1})) = 1",
                    proxy.getName());
            wrapper.apply(template, proxy.getValue(), proxy.getRawDetailValue("tag_id"));
        });
        supportedOperators.put(ConditionConstant.Operators.NE, (proxy, wrapper) -> {
            String template = String.format("JSON_CONTAINS(%s, JSON_OBJECT('id', {0}, 'label_id', {1})) = 1",
                    proxy.getName());
            wrapper.not(innerWrapper -> innerWrapper.apply(template, proxy.getValue(),
                    proxy.getRawDetailValue("tag_id")));
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> {
            String template = String.format("JSON_CONTAINS(%s, JSON_OBJECT('label_id', {0})) = 1", proxy.getName());
            wrapper.not(innerWrapper -> innerWrapper.apply(template, proxy.getRawDetailValue("tag_id")));
        });
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> {
            String template = String.format("JSON_CONTAINS(%s, JSON_OBJECT('label_id', {0})) = 1", proxy.getName());
            wrapper.apply(template, proxy.getRawDetailValue("tag_id"));
        });
        supportedTypes.put(ConditionConstant.FieldTypes.LABEL, supportedOperators);

        // 分组
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.BELONG, (proxy, wrapper) -> {
            Object value = proxy.getValue();
            if (value == null) {
                String msg = "condition.value missing for group";
                throw new ConditionException(msg);
            }
            String template = String.format("JSON_CONTAINS(%s, (SELECT JSON_ARRAY(seq) FROM ds_member_group WHERE id = UNHEX({0})), '$') = 1", proxy.getName());
            wrapper.apply(template, value);
        });
        supportedOperators.put(ConditionConstant.Operators.NBELONG, (proxy, wrapper) -> {
            Object value = proxy.getValue();
            if (value == null) {
                String msg = "condition.value missing for group";
                throw new ConditionException(msg);
            }
            String template = String.format("JSON_CONTAINS(%s, (SELECT JSON_ARRAY(seq) FROM ds_member_group WHERE id = UNHEX({0})), '$') = 0", proxy.getName());
            wrapper.apply(template, value);
        });
        supportedTypes.put(ConditionConstant.FieldTypes.GROUP, supportedOperators);

        // 地址
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EQ, (proxy, wrapper) -> wrapper.eq(proxy.getAddressName(),
                proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.NE, (proxy, wrapper) -> wrapper.isNull(proxy.getAddressName())
                .or().ne(proxy.getAddressName(), proxy.getValue()));
        supportedOperators.put(ConditionConstant.Operators.REGEX, (proxy, wrapper) -> {
            String template = String.format("%s regexp {0}", proxy.getAddressName());
            wrapper.apply(template, proxy.getStringValue());
        });
        supportedOperators.put(ConditionConstant.Operators.NREGEX, (proxy, wrapper) -> {
            String template = String.format("%s not regexp {0}", proxy.getAddressName());
            wrapper.isNull(proxy.getAddressName()).or().apply(template, proxy.getStringValue());
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> wrapper
                .isNull(proxy.getAddressName()));
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> wrapper
                .isNotNull(proxy.getAddressName()));
        supportedTypes.put(ConditionConstant.FieldTypes.ADDRESS, supportedOperators);

        // 扩展字段.
        supportedOperators = new HashMap<>();
        supportedOperators.put(ConditionConstant.Operators.EXISTS, (proxy, wrapper) -> {
            JSONObject detail = proxy.getDetail();
            if (detail == null) {
                throw new ConditionException("condition.detail missing");
            }
            String fieldId = detail.getString("field_id");

            wrapper.apply("extend_field_exists(field_val_list ,{0}) = 1", fieldId);
        });
        supportedOperators.put(ConditionConstant.Operators.NEXISTS, (proxy, wrapper) -> {
            JSONObject detail = proxy.getDetail();
            if (detail == null) {
                throw new ConditionException("condition.detail missing");
            }
            String fieldId = detail.getString("field_id");

            wrapper.apply("extend_field_exists(field_val_list ,{0}) = 0", fieldId);
        });
        supportedTypes.put(ConditionConstant.FieldTypes.FIELD_VAL_LIST, supportedOperators);
    }

    /**
     * 字段映射
     */
    private Map<String, ColumnDesc> columnMapping = new HashMap<>();

    /**
     * 数据表别名
     */
    private String tableAlias;

    public SQLConditionCompiler(Map<String, ColumnDesc> columnMapping, String tableAlias) {
        if (Objects.nonNull(columnMapping)) {
            this.columnMapping = columnMapping;
        }
        this.tableAlias = tableAlias;
    }

    /**
     * 创建实例
     *
     * @return
     */
    public static SQLConditionCompiler newInstance() {
        return new SQLConditionCompiler(null, null);
    }

    /**
     * 创建实例
     *
     * @param columnMapping
     * @return
     */
    public static SQLConditionCompiler newInstance(Map<String, ColumnDesc> columnMapping) {
        return new SQLConditionCompiler(columnMapping, null);
    }

    /**
     * 创建实例
     *
     * @return
     */
    public static SQLConditionCompiler newInstance(Map<String, ColumnDesc> columnMapping, String tableAlias) {
        return new SQLConditionCompiler(columnMapping, tableAlias);
    }

    /**
     * 编译条件
     *
     * @param condition
     * @return
     */
    public <T> ServiceResult<Wrapper<T>> compile(JSONObject condition) {
        try {
            // 解析条件
            QueryWrapper<Object> wrapper = new QueryWrapper<>();
            this.onCompoundCompile(wrapper, condition, 0);
            if (!wrapper.isEmptyOfNormal()) {
                return ServiceResult.success((Wrapper<T>) wrapper);
            } else {
                log.error("condition compile failed, no result, condition: {}", condition);
            }
        } catch (BaseRuntimeException e) {
            log.error("condition compile failed, errorCode: {}, reason: {}, condition: {}",
                    e.getCode(), e.getDesc(), condition, e);
            if (StringUtils.equals(e.getCode(), ErrorCodes.A0400)) {
                return ServiceResult.<Wrapper<T>>fail(e.getDesc()).setErrorCode(ErrorCodes.A0400);
            }
        } catch (Exception e) {
            log.error("condition compile failed, reason unknown, condition: {}", condition, e);
        }
        return ServiceResult.<Wrapper<T>>fail("条件解析失败").setErrorCode(ErrorCodes.A0400);
    }

    /**
     * 复合条件编译
     *
     * @param wrapper
     * @param condition
     * @param level
     */
    private void onCompoundCompile(QueryWrapper<Object> wrapper, JSONObject condition, int level) {
        if (condition == null) {
            throw new ConditionException("condition missing");
        }
        if (level > ConditionConstant.MAX_LEVEL) {
            throw new ConditionException(ErrorCodes.A0400, "嵌套层级不能超过三层");
        }

        String type = condition.getString("type");
        String rule = condition.getString("rule");
        String source = condition.getString("source");
        if (StringUtils.isNotBlank(rule)) {
            level += 1;
            List<JSONObject> filters = (List<JSONObject>) condition.get("filters");
            if (CollectionUtils.isEmpty(filters)) {
                throw new ConditionException("condition group must have filters");
            }
            // 每个条件组最多包含5个条件或条件组
            if (filters.size() > 5) {
                throw new ConditionException(ErrorCodes.A0400, "每个条件组最多包含5个条件或条件组");
            }

            // 编译条件组
            int finalLevel = level;
            if (StringUtils.equalsIgnoreCase(rule, "$or")) {
                filters.stream().filter(Objects::nonNull)
                        .forEach(filter -> wrapper.or(innerWrapper -> this.onCompoundCompile(innerWrapper,
                                filter, finalLevel)));
            } else if (StringUtils.equalsIgnoreCase(rule, "$and")) {
                filters.stream().filter(Objects::nonNull)
                        .forEach(filter -> wrapper.and(innerWrapper -> this.onCompoundCompile(innerWrapper,
                                filter, finalLevel)));
            } else {
                throw new ConditionException(String.format("unsupported rule %s", rule));
            }
        } else if (StringUtils.equals(source, ConditionConstant.Sources.INFO)
                || StringUtils.equals(source, ConditionConstant.Sources.PROJECT)) { // 联系人基本信息或项目信息
            String fieldName = condition.getString(ConditionConstant.NAME);

            // 扩展字段列表
            if (StringUtils.equals(fieldName, ConditionConstant.FIELD_VAL_LIST_FIELD_VALUE)) {
                String operator = condition.getString(ConditionConstant.OPERATOR);
                Map<String, BiConsumer<ConditionProxy, QueryWrapper<Object>>> supportedOperators = supportedTypes
                        .get(ConditionConstant.FieldTypes.FIELD_VAL_LIST);
                if (supportedOperators.containsKey(operator)) {
                    this.onSimpleCompile(wrapper, ConditionConstant.FieldTypes.FIELD_VAL_LIST, condition);
                    return;
                }

                this.onExtendFieldCompile(wrapper, condition);
            } else {
                this.onSimpleCompile(wrapper, type, condition);
            }
        } else if (StringUtils.equals(source, ConditionConstant.Sources.TAG)) { // 标签信息
            this.onSimpleCompile(wrapper, ConditionConstant.Sources.TAG, condition);
        } else if (StringUtils.equals(source, ConditionConstant.Sources.LABEL)) { // 标签信息
            this.onSimpleCompile(wrapper, ConditionConstant.Sources.LABEL, condition);
        } else if (StringUtils.equals(source, ConditionConstant.Sources.GROUP)) { // 标签信息
            this.onSimpleCompile(wrapper, ConditionConstant.Sources.GROUP, condition);
        } else if (StringUtils.equals(source, ConditionConstant.Sources.COMPUTES)) { // 计算信息
            this.onSimpleCompile(wrapper, type, condition);
        } else if (StringUtils.equals(source, ConditionConstant.Sources.SURVEY)) { // 答卷信息
            // skip
        } else { // 不支持的数据来源
            throw new ConditionException("unsupported data source");
        }
    }

    /**
     * 扩展字段编译
     *
     * @param wrapper
     * @param condition
     */
    private void onExtendFieldCompile(QueryWrapper<Object> wrapper, JSONObject condition) {
        ConditionProxy proxy = new ConditionProxy(condition, columnMapping);
        proxy.setTableAlias(tableAlias);
        JSONObject detail = proxy.getDetail();
        if (detail == null) {
            throw new ConditionException("condition.detail missing");
        }
        String fieldId = detail.getString("field_id");

        String type = condition.getString(ConditionConstant.TYPE);
        if (StringUtils.isBlank(type)) {
            throw new ConditionException("condition.type missing");
        }
        String operator = condition.getString(ConditionConstant.OPERATOR);
        if (StringUtils.isBlank(operator)) {
            throw new ConditionException("condition.operator missing");
        }

        if (StringUtils.equals(type, ConditionConstant.FieldTypes.STRING)) {
            String template = "get_extend_field_value(field_val_list, {0}) %s {1}";
            if (StringUtils.equals(operator, ConditionConstant.Operators.EQ)) {
                wrapper.apply(String.format(template, "="), fieldId, proxy.getStringValue());
            } else if (StringUtils.equals(operator, ConditionConstant.Operators.NE)) {
                template = "get_extend_field_value(field_val_list, {0}) is null or " + template;
                wrapper.apply(String.format(template, "!="), fieldId, proxy.getStringValue());
            } else if (StringUtils.equals(operator, ConditionConstant.Operators.REGEX)) {
                wrapper.apply(String.format(template, "regexp"), fieldId, proxy.getStringValue());
            } else if (StringUtils.equals(operator, ConditionConstant.Operators.NREGEX)) {
                template = "get_extend_field_value(field_val_list, {0}) is null or " + template;
                wrapper.apply(String.format(template, "not regexp"), fieldId, proxy.getStringValue());
            } else {
                throw new ConditionException(String.format("condition.operator unsupported, %s", operator));
            }
        } else if (StringUtils.equals(type, ConditionConstant.FieldTypes.DATETIME)) {
            if (StringUtils.equals(operator, ConditionConstant.Operators.RANGE)) {
                List<Object> values = proxy.listValues();
                if (CollectionUtils.isEmpty(values) || values.size() != 2) {
                    throw new ConditionException(String.format("condition.value %s illegal", values));
                }

                Object leftValue = values.get(0);
                Object rightValue = values.get(1);
                if (leftValue == null || rightValue == null) {
                    String msg = String.format("condition.value missing for $range, left: %s, right: %s",
                            leftValue, rightValue);
                    throw new ConditionException(msg);
                }

                String leftOperator = proxy.isLeftOpen() ? ">" : ">=";
                String rightOperator = proxy.isRightOpen() ? "<" : "<=";
                String column = "str_to_date(get_extend_field_value(field_val_list, {0}), '%Y/%m/%d %H:%i:%s')";
                StringBuilder buf = new StringBuilder();
                buf.append(column).append(StringUtils.SPACE).append(leftOperator).append(StringUtils.SPACE)
                        .append("{1}");
                buf.append(StringUtils.SPACE).append("and").append(StringUtils.SPACE);
                buf.append(column).append(StringUtils.SPACE).append(rightOperator).append(StringUtils.SPACE)
                        .append("{2}");
                wrapper.apply(buf.toString(), fieldId, leftValue, rightValue);
            }
        } else if (StringUtils.equals(type, ConditionConstant.FieldTypes.NUMBER)) {
            if (StringUtils.equals(operator, ConditionConstant.Operators.RANGE)) {
                List<Object> values = proxy.listValues();
                if (CollectionUtils.isEmpty(values) || values.size() != 2) {
                    throw new ConditionException(String.format("condition.value %s illegal", values));
                }

                Object leftValue = values.get(0);
                Object rightValue = values.get(1);
                if (leftValue == null || rightValue == null) {
                    String msg = String.format("condition.value missing for $range, left: %s, right: %s",
                            leftValue, rightValue);
                    throw new ConditionException(msg);
                }

                String leftOperator = proxy.isLeftOpen() ? ">" : ">=";
                String rightOperator = proxy.isRightOpen() ? "<" : "<=";
                String column = "convert(get_extend_field_value(field_val_list, {0}), signed)";
                StringBuilder buf = new StringBuilder();
                buf.append(column).append(StringUtils.SPACE).append(leftOperator).append(StringUtils.SPACE)
                        .append("{1}");
                buf.append(StringUtils.SPACE).append("and").append(StringUtils.SPACE);
                buf.append(column).append(StringUtils.SPACE).append(rightOperator).append(StringUtils.SPACE)
                        .append("{2}");
                wrapper.apply(buf.toString(), fieldId, leftValue, rightValue);
            } else {
                String sqlOperator = DecoderUtil.<String, String>create(operator)
                        .decode(ConditionConstant.Operators.GT, ">")
                        .decode(ConditionConstant.Operators.GTE, ">=")
                        .decode(ConditionConstant.Operators.LT, "<")
                        .decode(ConditionConstant.Operators.LTE, "<=")
                        .decode(ConditionConstant.Operators.EQ, "=")
                        .decode(ConditionConstant.Operators.NE, "!=")
                        .defaults(null).get();
                if (StringUtils.isBlank(sqlOperator)) {
                    throw new ConditionException(String.format("condition.operator unsupported, %s", operator));
                }

                String template = "convert(get_extend_field_value(field_val_list, {0}), signed) %s {1}";
                if (StringUtils.equals(operator, ConditionConstant.Operators.NE)) {
                    template = "get_extend_field_value(field_val_list, {0}) is null or " + template;
                }
                wrapper.apply(String.format(template, sqlOperator), fieldId, proxy.getValue());
            }
        } else if (StringUtils.equals(type, ConditionConstant.FieldTypes.SELECT)) {
            String sqlOperator = DecoderUtil.<String, String>create(operator)
                    .decode(ConditionConstant.Operators.EQ, "=")
                    .decode(ConditionConstant.Operators.NE, "!=")
                    .defaults(null).get();
            if (StringUtils.isBlank(sqlOperator)) {
                throw new ConditionException(String.format("condition.operator unsupported, %s", operator));
            }

            String template = "get_extend_field_value(field_val_list, {0}) %s {1}";
            if (StringUtils.equals(operator, ConditionConstant.Operators.NE)) {
                template = "get_extend_field_value(field_val_list, {0}) is null or " + template;
            }
            wrapper.apply(String.format(template, sqlOperator), fieldId, proxy.getValue());
        }
    }

    /**
     * 简单条件编译
     *
     * @param wrapper
     * @param type
     * @param condition
     */
    public void onSimpleCompile(QueryWrapper<Object> wrapper, String type, JSONObject condition) {
        if (StringUtils.isBlank(type)) {
            throw new ConditionException("condition.type missing");
        }

        Map<String, BiConsumer<ConditionProxy, QueryWrapper<Object>>> supportedOperators = supportedTypes.get(type);
        if (CollectionUtils.isEmpty(supportedOperators)) {
            throw new ConditionException(String.format("condition.type %s unsupported", type));
        }

        String operator = condition.getString(ConditionConstant.OPERATOR);
        if (StringUtils.isBlank(operator)) {
            throw new ConditionException("condition.operator missing");
        }
        BiConsumer<ConditionProxy, QueryWrapper<Object>> processor = supportedOperators.get(operator);
        if (processor == null) {
            throw new ConditionException(String.format("condition.operator %s unsupported", operator));
        }

        ConditionProxy conditionProxy = new ConditionProxy(condition, columnMapping);
        conditionProxy.setTableAlias(tableAlias);
        processor.accept(conditionProxy, wrapper);
    }

    /**
     * 预处理编译条件
     *
     * @param condition
     * @return
     */
    public <T> ServiceResult<Wrapper<T>> compile(JSONObject condition, ConditionPreprocessor preprocessor) {
        try {
            // 解析条件
            QueryWrapper<Object> wrapper = new QueryWrapper<>();
            this.preprocessCompile(wrapper, condition, preprocessor);
            if (!wrapper.isEmptyOfNormal()) {
                return ServiceResult.success((Wrapper<T>) wrapper);
            } else {
                log.error("condition compile failed, no result, condition: {}", condition);
            }
        } catch (BaseRuntimeException e) {
            log.error("condition compile failed, errorCode: {}, reason: {}, condition: {}",
                    e.getCode(), e.getDesc(), condition, e);
            if (StringUtils.equals(e.getCode(), ErrorCodes.A0400)) {
                return ServiceResult.<Wrapper<T>>fail(e.getDesc()).setErrorCode(ErrorCodes.A0400);
            }
        } catch (Exception e) {
            log.error("condition compile failed, reason unknown, condition: {}", condition, e);
        }
        return ServiceResult.<Wrapper<T>>fail("条件解析失败").setErrorCode(ErrorCodes.A0400);
    }

    /**
     * 预处理条件解析
     *
     * @param filterCondition condition
     * @param preprocessor    预处理器
     * @return 解析结果
     */
    public String preprocessCompile(QueryWrapper<Object> wrapper, JSONObject filterCondition, ConditionPreprocessor preprocessor) {
        int level = Optional.ofNullable(filterCondition.getInteger("level")).orElse(0);
        final StringBuilder sb = new StringBuilder();
        String rule = filterCondition.getString("rule");
        if(StringUtils.isNotBlank(rule)){
            List<JSONObject> filters = (List<JSONObject>) filterCondition.get("filters");
            if (CollectionUtils.isEmpty(filters)) {
                throw new ConditionException("condition group must have filters");
            }
            // 每个条件组最多包含5个条件或条件组
            if (filters.size() > 5) {
                throw new ConditionException(ErrorCodes.A0400, "每个条件组最多包含5个条件或条件组");
            }

            filterCondition.put("level", level + 1);
            // 编译条件组
            if (StringUtils.equalsIgnoreCase(rule, "$or")) {
                filters.stream().filter(Objects::nonNull)
                        .forEach(filter -> wrapper.or(innerWrapper -> this.preprocessCompile(innerWrapper,
                                filter, preprocessor)));
            } else if (StringUtils.equalsIgnoreCase(rule, "$and")) {
                filters.stream().filter(Objects::nonNull)
                        .forEach(filter -> wrapper.and(innerWrapper -> this.preprocessCompile(innerWrapper,
                                filter, preprocessor)));
            } else {
                throw new ConditionException(String.format("unsupported rule %s", rule));
            }

        }else{
            // 预处理
            sb.append(preprocessor.preprocessing(filterCondition));
            this.onCompoundCompile(wrapper, filterCondition, level);
        }

        return sb.toString();
    }

    /**
     * condition 预处理器
     */
    @FunctionalInterface
    public interface ConditionPreprocessor {
        /**
         * condition 预处理方法
         *
         * @param jsonConditionUnit condition
         * @return errorMsg
         */
        String preprocessing(JSONObject jsonConditionUnit);
    }
}
